/**
 * Copyright &copy; 2016-2017 <a href="http://git.oschina.net/whoamien/backend_management">Backend Management</a> All rights reserved.
 */
package com.xw.app.generator.vo;

import java.sql.Types;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Lists;
import com.xw.app.generator.util.DataTypeUtil;
import com.xw.util.JsonUtil;
import com.xw.util.StringUtil;

/**
 * The Class Column.
 *
 * @author Xi Wei
 */
public class Column {

	private String tableSchema;
	private String tableName;
	private String columnName;
	private int ordinalPosition;
	private String columnDefault;
	private boolean isNullable;
	private String dataType;
	private Integer characterOctetLength;
	private Integer numericPrecision;
	private Integer numericScale;
	private Integer datetimePrecision;
	private int sqlType;
	private int columnSize;
	private String columnKey;
	private String columnComment;	
	private Table table;//字段所属的表对象
	private String dictCode;//如果字段是从数据字典中取值，值在哪种数据字典中
	private String referenceDisplayColumn;//如果是外键，在页面显示的属性
	private KeyColumn referenceColumn;
	
	private String hibernateValidateString;
	private List<String> importList = Lists.newArrayList();//需要导入的包
	private List<String> validateImportList = Lists.newArrayList();//实体bean验证需要导入的包
	private List<String> queryImportList = Lists.newArrayList();//查询Bean需要导入的包
	private List<String> annotationList = Lists.newArrayList();//字段需要的注解
	private List<String> jsValidationList = Lists.newArrayList();//前端JS需要的验证
	
	
	/**
	 * Gets the table schema.
	 *
	 * @return the table schema
	 */
	public String getTableSchema() {
		return tableSchema;
	}
	
	/**
	 * Sets the table schema.
	 *
	 * @param tableSchema the new table schema
	 */
	public void setTableSchema(String tableSchema) {
		this.tableSchema = tableSchema;
	}
	
	/**
	 * Gets the table name.
	 *
	 * @return the table name
	 */
	public String getTableName() {
		return tableName;
	}
	
	/**
	 * Sets the table name.
	 *
	 * @param tableName the new table name
	 */
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	
	/**
	 * Gets the column name.
	 *
	 * @return the column name
	 */
	public String getColumnName() {
		return columnName;
	}
	
	/**
	 * Sets the column name.
	 *
	 * @param columnName the new column name
	 */
	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}
	
	/**
	 * Gets the ordinal position.
	 *
	 * @return the ordinal position
	 */
	public int getOrdinalPosition() {
		return ordinalPosition;
	}
	
	/**
	 * Sets the ordinal position.
	 *
	 * @param ordinalPosition the new ordinal position
	 */
	public void setOrdinalPosition(int ordinalPosition) {
		this.ordinalPosition = ordinalPosition;
	}
	
	/**
	 * Gets the column default.
	 *
	 * @return the column default
	 */
	public String getColumnDefault() {
		return columnDefault;
	}
	
	/**
	 * Sets the column default.
	 *
	 * @param columnDefault the new column default
	 */
	public void setColumnDefault(String columnDefault) {
		this.columnDefault = columnDefault;
	}
	
	/**
	 * Checks if is nullable.
	 *
	 * @return true, if is nullable
	 */
	public boolean isNullable() {
		return isNullable;
	}
	
	/**
	 * Sets the nullable.
	 *
	 * @param isNullable the new nullable
	 */
	public void setNullable(boolean isNullable) {
		this.isNullable = isNullable;
	}
	
	/**
	 * Gets the data type.
	 *
	 * @return the data type
	 */
	public String getDataType() {
		return dataType;
	}
	
	/**
	 * Sets the data type.
	 *
	 * @param dataType the new data type
	 */
	public void setDataType(String dataType) {
		this.dataType = dataType;
	}
	
	/**
	 * Gets the column size.
	 *
	 * @return the column size
	 */
	public int getColumnSize() {
		return columnSize;
	}
	
	/**
	 * Sets the column size.
	 *
	 * @param columnSize the new column size
	 */
	public void setColumnSize(int columnSize) {
		this.columnSize = columnSize;
	}
	
	/**
	 * Gets the character octet length.
	 *
	 * @return the character octet length
	 */
	public Integer getCharacterOctetLength() {
		return characterOctetLength;
	}
	
	/**
	 * Sets the character octet length.
	 *
	 * @param characterOctetLength the new character octet length
	 */
	public void setCharacterOctetLength(Integer characterOctetLength) {
		this.characterOctetLength = characterOctetLength;
	}
	
	/**
	 * Gets the numeric precision.
	 *
	 * @return the numeric precision
	 */
	public Integer getNumericPrecision() {
		return numericPrecision;
	}
	
	/**
	 * Sets the numeric precision.
	 *
	 * @param numericPrecision the new numeric precision
	 */
	public void setNumericPrecision(Integer numericPrecision) {
		this.numericPrecision = numericPrecision;
	}
	
	/**
	 * Gets the numeric scale.
	 *
	 * @return the numeric scale
	 */
	public Integer getNumericScale() {
		return numericScale;
	}
	
	/**
	 * Sets the numeric scale.
	 *
	 * @param numericScale the new numeric scale
	 */
	public void setNumericScale(Integer numericScale) {
		this.numericScale = numericScale;
	}
	
	/**
	 * Gets the datetime precision.
	 *
	 * @return the datetime precision
	 */
	public Integer getDatetimePrecision() {
		return datetimePrecision;
	}
	
	/**
	 * Sets the datetime precision.
	 *
	 * @param datetimePrecision the new datetime precision
	 */
	public void setDatetimePrecision(Integer datetimePrecision) {
		this.datetimePrecision = datetimePrecision;
	}
	
	
	/**
	 * Gets the sql type.
	 *
	 * @return the sql type
	 */
	public int getSqlType() {
		return sqlType;
	}
	
	/**
	 * Sets the sql type.
	 *
	 * @param sqlType the new sql type
	 */
	public void setSqlType(int sqlType) {
		this.sqlType = sqlType;
	}
	
	/**
	 * Gets the column key.
	 *
	 * @return the column key
	 */
	public String getColumnKey() {
		return columnKey;
	}
	
	/**
	 * Sets the column key.
	 *
	 * @param columnKey the new column key
	 */
	public void setColumnKey(String columnKey) {
		this.columnKey = columnKey;
	}
	
	/**
	 * Gets the column comment.
	 *
	 * @return the column comment
	 */
	public String getColumnComment() {
		return columnComment;
	}
	
	/**
	 * Sets the column comment.
	 *
	 * @param columnComment the new column comment
	 */
	public void setColumnComment(String columnComment) {
		this.columnComment = columnComment;
	}
	
	/**
	 * Gets the table.
	 *
	 * @return the table
	 */
	@JsonIgnore
	public Table getTable() {
		return table;
	}
	
	/**
	 * Sets the table.
	 *
	 * @param table the new table
	 */
	public void setTable(Table table) {
		this.table = table;
	}
	
	/**
	 * Gets the dict code.
	 *
	 * @return the dict code
	 */
	public String getDictCode() {
		return dictCode;
	}
	
	/**
	 * Sets the dict code.
	 *
	 * @param dictCode the new dict code
	 */
	public void setDictCode(String dictCode) {
		this.dictCode = dictCode;
	}
	
	/**
	 * Gets the reference display column.
	 *
	 * @return the reference display column
	 */
	public String getReferenceDisplayColumn() {
		return referenceDisplayColumn;
	}
	
	/**
	 * Sets the reference display column.
	 *
	 * @param referenceDisplayColumn the new reference display column
	 */
	public void setReferenceDisplayColumn(String referenceDisplayColumn) {
		this.referenceDisplayColumn = referenceDisplayColumn;
	}
	
	/**
	 * Gets the reference column.
	 *
	 * @return the reference column
	 */
	public KeyColumn getReferenceColumn() {
		return referenceColumn;
	}
	
	/**
	 * Sets the reference column.
	 *
	 * @param referenceColumn the new reference column
	 */
	public void setReferenceColumn(KeyColumn referenceColumn) {
		this.referenceColumn = referenceColumn;
	}
	
	/////////////////////////////
	
	
	
	/**
	 * Checks if is pk.
	 *
	 * @return true, if is pk
	 */
	public boolean isPk() {
		if(table == null || table.getPrimaryKeyList() == null || table.getPrimaryKeyList().isEmpty()){
			return false;
		}
		if(table.getPrimaryKeyColumnNameList().contains(columnName))
		{
			return true;
		}
		return false;
	}
	
	/**
	 * Gets the import list(the basic data type that require import class)
	 *
	 * @return the import list
	 */
	public List<String> getImportList() {
		return importList;
	}
	
	
	/**
	 * Gets the validate import list.(The import class required for the hibernate validation)
	 *
	 * @return the validate import list
	 */
	public List<String> getValidateImportList() {
		return validateImportList;
	}
	
	/**
	 * Checks if is fk.
	 *
	 * @return true, if is fk
	 */
	public boolean isFk() {
		if(table == null || table.getImportedKeyList() == null || table.getImportedKeyList().isEmpty()){
			return false;
		}
		if(table.getImportedKeyColumnNameList().contains(columnName)){
			return true;
		}
		return false;
	}
	
	/**
	 * Gets the display name.(if the comment is not empty, use the comment, else use the camel case of the column name
	 *
	 * @return the display name
	 */
	public String getDisplayNameEnglish() {
		return StringUtil.toCamelCaseWithSpace(columnName);
	}
	
	public String getDisplayNameChinese() {
		if(StringUtil.isNullOrEmpty(columnComment)){
			return StringUtil.toCamelCaseWithSpace(columnName);
		}else{
			return columnComment;
		}
		
	}
	
	/**
	 * Gets the hibernate validate string used in the model class
	 *
	 * @return the hibernate validate string
	 */
	public String getHibernateValidateString() {
		return hibernateValidateString;
	}
	
	/**
	 * Gets the java type of the column
	 *
	 * @return the java type
	 */
	public String getJavaType(){
		return DataTypeUtil.getJavaType(sqlType);
	}
	
	/**
	 * Gets the simple java type, just use the class name of the type not include the package
	 *
	 * @return the simple java type
	 */
	public String getSimpleJavaType(){
		String javaType = getJavaType();
		return StringUtil.substringAfterLast(javaType, ".");
	}
	
	private static List<String> likeQueryColumnList = Arrays.asList("NAME","TITLE","REMARK","DESC");
	
	/**
	 * Gets the query type(Operator.LIKE or null)
	 *
	 * @return the query type
	 */
	public String getQueryType(){
		Optional<String> search=likeQueryColumnList.stream().filter(c -> columnName != null && columnName.toUpperCase().contains(c)).findFirst();
		if(!search.isPresent()){
			return null;
		}
		return "Operator.LIKE";
	}
	
	/**
	 * Inits the column def info.
	 */
	public void initColumnDefInfo(){
		if(columnName.toLowerCase().contains("mail") || columnName.toLowerCase().contains("email")){
			annotationList.add("@Email");
			validateImportList.add("org.hibernate.validator.constraints.Email");
			jsValidationList.add("email='true'");
		}
		if("String".equals(getJavaType())){
			annotationList.add(MessageFormat.format("@Length(max={0,number,#})",columnSize ));
			validateImportList.add("org.hibernate.validator.constraints.Length");
			jsValidationList.add(MessageFormat.format("maxlength=''{0}''", columnSize));
		}
		if(DataTypeUtil.isNumberColumn(getJavaType())){
			switch(getJavaType()){
			case "Byte":
				annotationList.add(MessageFormat.format("@Max({0,number,#})", Byte.MAX_VALUE));
				validateImportList.add("javax.validation.constraints.Max");
				jsValidationList.add(MessageFormat.format("max=''{0,number,#}''", Byte.MAX_VALUE));
				break;
			case "Short":
				annotationList.add(MessageFormat.format("@Max({0,number,#})", Short.MAX_VALUE));
				validateImportList.add("javax.validation.constraints.Max");
				jsValidationList.add(MessageFormat.format("max=''{0,number,#}''", Short.MAX_VALUE));
				break;
			case "Integer":
				annotationList.add(MessageFormat.format("@Max({0,number,#})", Integer.MAX_VALUE));
				validateImportList.add("javax.validation.constraints.Max");
				jsValidationList.add(MessageFormat.format("max=''{0,number,#}''", Integer.MAX_VALUE));
				break;				
			}			
		}
		if(!isPk() && !isNullable()){
			jsValidationList.add("required='true'");
			if(DataTypeUtil.isStringColumn(getJavaType())){
				annotationList.add("@NotBlank");
				validateImportList.add("org.hibernate.validator.constraints.NotBlank");				
			}else{
				annotationList.add("@NotNull");
				validateImportList.add("javax.validation.constraints.NotNull");
			}
		}
		
		if(DataTypeUtil.isDateColumn(getJavaType())){
			queryImportList.add("org.springframework.format.annotation.DateTimeFormat");
			queryImportList.add("java.util.Date");
		}
		
		
		if(getJavaType().indexOf(".") > 0){
			importList.add(getJavaType());
			if(!isDateField())
			{
				queryImportList.add(getJavaType());
			}
		}
		
	}
	
	/**
	 * Gets the query import list.
	 *
	 * @return the query import list
	 */
	public List<String> getQueryImportList(){
		return queryImportList;
	}
	
	/**
	 * Gets the annotation list.
	 *
	 * @return the annotation list
	 */
	public List<String> getAnnotationList(){		
		return annotationList;
	}
	
	/**
	 * Gets the js validation.
	 *
	 * @return the js validation
	 */
	public String getJsValidation() {
		return jsValidationList.stream().collect(Collectors.joining(" "));
	}
	
	/**
	 * Gets the java field.
	 *
	 * @return the java field
	 */
	public String getJavaField(){
		return StringUtil.toCamelCase(columnName);
	}
	
	private List<String> baseFieldList = Arrays.asList("createDate","createUserId","updateDate","updateUserId","id");
	private List<String> nonQueryFieldList = Arrays.asList("createDate","createUserId","updateDate","updateUserId","id");
	
	/**
	 * Checks if is base field.
	 *
	 * @return true, if is base field
	 */
	public boolean isBaseField(){
		if(baseFieldList.contains(getJavaField())){
			return true;
		}
		return false;
	}
	
	/**
	 * Checks if is query field.
	 *
	 * @return true, if is query field
	 */
	public boolean isQueryField(){
		if(nonQueryFieldList.contains(getJavaField())){
			return false;
		}
		return true;
	}
	
	/**
	 * Checks if is date field.
	 *
	 * @return true, if is date field
	 */
	public boolean isDateField(){
		return DataTypeUtil.isDateColumn(getJavaType());
	}
	
	/**
	 * Checks if is sql date field.
	 *
	 * @return true, if is sql date field
	 */
	public boolean isSqlDateField(){
		if(getJavaType().equalsIgnoreCase("java.sql.Date")){
			return true;
		}
		return false;
	}
	
	/**
	 * Checks if is time field.
	 *
	 * @return true, if is time field
	 */
	public boolean isTimeField(){
		if(getJavaType().equalsIgnoreCase("java.sql.Time")){
			return true;
		}
		return false;
	}
	
	/**
	 * Checks if is timestamp field.
	 *
	 * @return true, if is timestamp field
	 */
	public boolean isTimestampField(){
		if(getJavaType().equalsIgnoreCase("java.sql.Timestamp")){
			return true;
		}
		return false;
	}
	
	/**
	 * Checks if is data dict field.
	 *
	 * @return true, if is data dict field
	 */
	public boolean isDataDictField(){
		if(this.dictCode == null){
			return false;
		}
		return true;
	}
	
	/**
	 * Check if is bit field
	 * @return true, if is bit field
	 */
	public boolean isBitField(){
		if(getJavaType().equalsIgnoreCase("boolean"))
		{
			return true;
		}
		return false;
	}
	
	/**
	 * Check if is a Text Area field
	 * @return true, if is a Text Area field
	 */
	public boolean isTextareaField(){
		if(getSqlType() == Types.LONGNVARCHAR || getSqlType() == Types.LONGVARCHAR){
			return true;
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return JsonUtil.beanToJson(this);
	}
	
	
}
